﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using My.SharePoint.STSADM.Commands.OperationHelpers;
using Microsoft.SharePoint;
using System.IO;
using System.Xml;

namespace IdeasFree.SharePoint.StsAdm.Commands.Helpers
{
    public class ImportDocumentOperation
    {
        string url;
        string doclib;
        string importFolder;
        string mapping;
        bool isMetaOnly = false;
        Dictionary<string, string> mappingMetadata;

        public ImportDocumentOperation(SPParamCollection Params, System.Collections.Specialized.StringDictionary keyValues)
        {
            url = Params["url"].Value;
            doclib = Params["doclib"].Value;
            importFolder = Params["importfolder"].Value;
            mapping = Params["mapping"].Value;
            bool.TryParse(Params["metaonly"].Value, out isMetaOnly);

            mappingMetadata = ReadMapping(mapping);
        }
        internal void Process()
        {
            using (SPSite spSite = new SPSite(url))
            {
                using (SPWeb spWeb = spSite.OpenWeb())
                {
                    spWeb.Lists.IncludeRootFolder = true;
                    SPList spList = spWeb.Lists[doclib];
                    
                    if (isMetaOnly)
                        ImportMetadataFromSystemFolder(spList, mappingMetadata, importFolder);
                    else
                        ImportFromFileSystemFolder(spList, mappingMetadata, importFolder);
                }
            }
        }

        private void ImportFromFileSystemFolder(SPList spList, Dictionary<string, string> mappingMetadata, string importFolder)
        {
            ImportFromFileSystemFolder(spList.RootFolder, mappingMetadata, importFolder);
        }
        private void ImportFromFileSystemFolder(SPFolder folder, Dictionary<string, string> mappingMetadata, string strImportPath)
        {

            // Import files from location, then create new folders if necassary and import those
            ImportFilesToFolder(folder, mappingMetadata, strImportPath);
            DirectoryInfo dir = new DirectoryInfo(strImportPath);

            foreach (DirectoryInfo subdir in dir.GetDirectories())
            {
                Console.WriteLine("Creating folder for " + subdir.Name);
                folder.SubFolders.Add(subdir.Name);
                SPFolder subfolder = folder.SubFolders[subdir.Name];
                // TODO: Add security code to mimic File System security to SPFolder 

                ImportFromFileSystemFolder(subfolder, mappingMetadata, subdir.FullName);
            }
        }
        /// <summary>
        /// Imports files from a specific path into a specific SPFolder
        /// </summary>
        /// <param name="folder">The SPFolder to place the files in</param>
        /// <param name="strImportPath">The File System Path (Directory) to get files from</param>
        private void ImportFilesToFolder(SPFolder folder, Dictionary<string, string> mappingMetadata, string strImportPath)
        {
            string msg = string.Empty;
            string strFileMask = "*.*";

            Console.WriteLine("Adding files to {0} from {1}", folder.Url, strImportPath);
            DirectoryInfo dir = new DirectoryInfo(strImportPath);

            foreach (FileInfo f in dir.GetFiles(strFileMask))
            {
                if (f.Extension.Equals(".xml", StringComparison.InvariantCultureIgnoreCase))
                    continue;
                try
                {
                    byte[] buffer = new byte[f.Length];
                    System.IO.FileStream fs = new FileStream(f.FullName, System.IO.FileMode.Open);
                    fs.Read(buffer, 0, (int)f.Length);
                    fs.Close();
                    Console.WriteLine("   Adding document {0}", f.Name);
                    SPFile spFile = folder.Files.Add(f.Name, buffer, true);
                    SPListItem item = spFile.Item;
                    Dictionary<string, string> fileMetadata = ReadMetadata(f.FullName + ".xml");
                    ImportFileMetadata(item, fileMetadata, mappingMetadata);

                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }

        private void ImportMetadataFromSystemFolder(SPList spList, Dictionary<string, string> mappingMetadata, string importFolder)
        {
            ImportMetadataFromSystemFolder(spList, spList.RootFolder.ServerRelativeUrl, mappingMetadata, importFolder);            
        }
        private void ImportMetadataFromSystemFolder(SPList spList, string folderUrl, Dictionary<string, string> mappingMetadata, string importFolder)
        {
            ImportFileMetadata(spList, folderUrl, mappingMetadata, importFolder);
            DirectoryInfo dir = new DirectoryInfo(importFolder);
            foreach (DirectoryInfo subDir in dir.GetDirectories())
            {
                SPFolder subfolder = Utilities.CreateFolder(spList, folderUrl.TrimEnd('/') + "/" + subDir.Name);
                ImportMetadataFromSystemFolder(spList, subfolder.ServerRelativeUrl, mappingMetadata, subDir.FullName);
            }
        }

        private void ImportFileMetadata(SPList spList, string spListFolderPath, Dictionary<string, string> mappingMetadata, string importFolder)
        {
            string msg = string.Empty;
            string strFileMask = "*.xml";
            Console.WriteLine("Processing file from folder {0}.", importFolder);
            DirectoryInfo dir = new DirectoryInfo(importFolder);

            foreach (FileInfo f in dir.GetFiles(strFileMask))
            {
                SPListItem item = null;

                Dictionary<string, string> fileMetadata = ReadMetadata(f.FullName);
                if (mappingMetadata.Keys.Contains("SPFolder"))
                {
                    SPFolder folder = Utilities.CreateFolder(spList, fileMetadata[mappingMetadata["SPFolder"]]);
                    item = folder.Item;
                }

                if (item == null)
                    item = spList.Items.Add(spListFolderPath, SPFileSystemObjectType.File);
                Console.WriteLine("   Importing metadata {0}. ", f.FullName);
                ImportFileMetadata(item, fileMetadata, mappingMetadata);
            }
        }

        private void ImportFileMetadata(SPListItem item, Dictionary<string, string> metadata, Dictionary<string, string> mappingMetadata)
        {
            using (IEnumerator<string> enumerator = mappingMetadata.Keys.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    string targetField = mappingMetadata[enumerator.Current];
                    if (targetField.Equals("SPFolder", StringComparison.InvariantCultureIgnoreCase))
                        continue;

                    if (item.Fields.ContainsField(targetField))
                    {
                        string valueField = metadata[enumerator.Current];
                        if (String.IsNullOrEmpty(valueField))
                            continue;
                        SetFieldValue(item, targetField, valueField);
                    }
                    else
                    {
                        Console.WriteLine("        Can not find {0} in target. Skipping.", mappingMetadata[enumerator.Current]);
                    }
                }
            }


            item.Update();
        }

        private Dictionary<string, string> ReadMapping(string mappingFile)
        {
            Dictionary<string, string> mappingResult = new Dictionary<string, string>();
            MappingMetadata meta = MappingMetadata.LoadFromFile(mappingFile);

            foreach (XmlAttribute attr in meta.Mapping.AnyAttr)
            {
                mappingResult.Add(attr.Name, attr.Value);
            }

            return mappingResult;
        }
        private Dictionary<string, string> ReadMetadata(string metadataFile)
        {
            Dictionary<string, string> metaDataResult = new Dictionary<string, string>();
            ListMeta meta = ListMeta.LoadFromFile(metadataFile);

            foreach (XmlAttribute attr in meta.ListItem.AnyAttr)
            {
                metaDataResult.Add(attr.Name, attr.Value);
            }

            return metaDataResult;
        }

        private void SetFieldValue(SPListItem item, string fieldName, string valueField)
        {
            SPFieldType itemFieldType = item.Fields[fieldName].Type;
            switch (itemFieldType)
            {
                case SPFieldType.Boolean:
                    bool boolValueField;
                    if (bool.TryParse(valueField, out boolValueField))
                        item[fieldName] = boolValueField;
                    break;
                case SPFieldType.DateTime:
                    DateTime dateTimeValueField;
                    if (DateTime.TryParse(valueField, out dateTimeValueField))
                        item[fieldName] = dateTimeValueField;
                    break;
                case SPFieldType.Lookup:
                    SPFieldLookup lookup = item.Fields[fieldName] as SPFieldLookup;
                    if (lookup.AllowMultipleValues)
                    {
                        List<string> values = valueField.Split(';').ToList<string>();
                        item.SetFieldValueLookup(fieldName, values);
                    }
                    else
                    {
                        item.SetFieldValueLookup(fieldName, valueField);
                    }
                    break;
                case SPFieldType.MultiChoice:
                    SPFieldMultiChoiceValue multiChoiceValue = new SPFieldMultiChoiceValue();
                    foreach (string choice in valueField.Split(','))
                        multiChoiceValue.Add(choice);
                    item[fieldName] = multiChoiceValue;
                    break;
                default:
                    item[fieldName] = valueField;
                    break;
            }
        }
    }
}
